package sync

import (
	"sync"
	"time"

	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/sync_protocol"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/serialize"
)

type memberHeap []*memberInstance

func (h *memberHeap) Less(i, j int) bool {
	return (*h)[i].RelationNum < (*h)[j].RelationNum
}

func (h *memberHeap) Swap(i, j int) {
	(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
}

func (h *memberHeap) Len() int {
	return len(*h)
}

func (h *memberHeap) Pop() (v interface{}) {
	*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
	return
}

func (h *memberHeap) Push(v interface{}) {
	*h = append(*h, v.(*memberInstance))
}

type itemInstance struct {
	Owner    string // client
	Belong   uint32 // item
	Agent    string // client
	Observer string // client

	Location  []byte          // 坐标
	Level     byte            // 位面
	Direction orbit.Direction // 区块内方向

	Meta []interface{} // 元数据, 不可变更，创建者提交
	Info []interface{} // 即时数据, 只保留最新结果：方向，速度，加速度等影响渲染的数据，agent进行更新

	Data       []interface{}          // 版本数据，提供给观察者进行更新的数据，提交给游戏端进行数据分析
	Version    byte                   // 当前版本
	DataExt    map[uint16]interface{} // 扩展data动态长度存储
	indexs     []interface{}
	datas      []interface{}
	changeData bool
	transfer   *cluster.Client

	Message []byte // 持久化消息
}
type memberInstance struct {
	OpenId      string
	Owner       map[uint32]bool
	Observer    map[uint32]bool
	Agent       sync.Map
	Node        string
	Delay       int
	Avail       bool
	RelationNum uint32

	Bucket int

	inHeap bool
}

type timeInstance struct {
	t       sync_protocol.TimeType
	Payload *serialize.Mpb
	End     int64
	Start   int64
	Next    string
}

func NewTime(instance *blockInstance, b *serialize.Mpb, node cluster.Node) *timeInstance {
	ti := &timeInstance{}

	// code byte location []byte info...
	// trigger: [time int, code byte, payload []]
	// move: [startTime int, endTime int, payload [], position [], index int]
	code := b.ReadUint32(0)
	tt := sync_protocol.TimeType(code)
	ti.t = tt
	switch sync_protocol.TimeType(code) {
	case sync_protocol.TriggerTime:
		ti.Trigger(instance, b)
	case sync_protocol.ProcessTime:
		ti.Process(instance, b)
		node.Broadcast().Data(b).Send(sync_protocol.Time)
	case sync_protocol.MoveTime:
		ti.Move(instance, b)
		node.Broadcast().Data(b).Send(sync_protocol.Time)
	}
	return ti
}

func (ti *timeInstance) Update(t int64, instance *blockInstance, node cluster.Node) bool {
	if t > ti.End {
		switch ti.t {
		case sync_protocol.TriggerTime:
			node.LocalEventAsync(ti.Payload.ReadByte(1), ti.Payload.ReadBytes(2), node.CurrentNode(), node.CurrentNode())
			ti.Payload.Release()
		case sync_protocol.ProcessTime:
			ti.Payload.Release()
		case sync_protocol.MoveTime:
			// 发送到下一个节点
			if ti.Next != "" {
				// 最后一个节点，不转发
				client := instance.Service.GetWithNodeId(node.Container(), ti.Next)
				client.Emit().Data(ti.Payload).Send(sync_protocol.Time)
			}
			ti.Payload.Release()
		}
		return true
	}
	return false
}

func (ti *timeInstance) Send(node cluster.Node) {
	switch ti.t {
	case sync_protocol.ProcessTime:
		node.Broadcast().Data(ti.Payload).Send(sync_protocol.Time)
	case sync_protocol.MoveTime:
		node.Broadcast().Data(ti.Payload).Send(sync_protocol.Time)
	}
}
func (ti *timeInstance) Trigger(instance *blockInstance, b *serialize.Mpb) {
	data := b.ReadBag(2)
	ti.End = data.ReadInt64(0)
	ti.Payload = data
	ti.Payload.Alloc()
	data.Release()
}

func (ti *timeInstance) Process(instance *blockInstance, b *serialize.Mpb) {
	//[startTime int, endTime int, payload []]
	data := b.ReadBag(2)

	start := data.ReadInt64(0)
	end := data.ReadInt64(1)

	ti.Start = start
	ti.End = end
	ti.Payload = b
	ti.Payload.Alloc()
	data.Release()
}

func (ti *timeInstance) Move(instance *blockInstance, b *serialize.Mpb) {
	//[startTime int, endTime int, payload [], position [], index int, startPosition?]
	t := time.Now().Unix()
	data := b.ReadBag(2)

	start := data.ReadInt64(0)
	end := data.ReadInt64(1)
	p := data.ReadInterface(3)
	positions := utils.ToBytesSlice(p)
	index := data.ReadInt(4)
	position := positions[index]

	if data.Len() > 5 {
		// 短接点：用短接点来代替路径的上一个节点
		position = data.ReadBytes(5)
	} else if index == 0 {
		// 如果是第一节点，并且没有短接点，则是第一次，开始时间由start确定
		t = start
	}

	location := orbit.LocationCM.DecodeLocation(position)
	ps := make([]*orbit.Location, len(positions))
	ps[index] = location

	var nl *orbit.Location
	var nb *orbit.BlockLocation
	for i := index + 1; i < len(positions); i++ {
		ps[i] = orbit.LocationCM.DecodeLocation(positions[i])
		if !instance.Block.Contain(ps[i]) {
			index = i - 1
			nb, nl = orbit.LocationCM.NextBlock(instance.Block, location, ps[i])
			if !nb.Contain(ps[i]) {
				// 更新新短接点
				if data.Len() > 5 {
					data.Replace(5, orbit.LocationCM.EncodeLocation(nl))
				} else {
					data.Push(orbit.LocationCM.EncodeLocation(nl))
				}
			} else {
				if data.Len() > 5 {
					// 移除短接点
					data.Pop()
				}
			}
			break
		}
		location = ps[i-1]
	}

	tt := end
	if nl != nil {
		distance := orbit.LocationCM.Distance(ps, data.ReadInt(4))
		diff := int32(end - t)
		speed := distance / float64(diff)
		tt = int64(ps[index].Distance(nl)/speed) + t
	} else {
		// 最后一个节点, 直接以end为结果A2	S1ΩΩΩΩ
	}

	data.Replace(4, index)
	b.Replace(2, data.Get())
	ti.Start = start
	ti.End = tt
	ti.Payload = b
	ti.Payload.Alloc()
	if nb != nil {
		ti.Next = instance.Service.GetNodeId(instance.Area, nb)
		orbit.LocationCM.ReleaseBlock(nb)
	}
	for i := range ps {
		if ps[i] == nil {
			continue
		}
		orbit.LocationCM.ReleaseLocation(ps[i])
	}
	data.Release()
}
